package com.qire.common.basic;

import com.qire.common.function.Function;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public enum DateUtil {

    HHmm(new SimpleDateFormat("HH:mm")),
    yyyy_MM_dd(new SimpleDateFormat("yyyy-MM-dd")),
    yyyy_MM_dd_HHmmss(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));

    public final SimpleDateFormat dateFormat;

    DateUtil(SimpleDateFormat dateFormat) {
        this.dateFormat = dateFormat;
    }

    /**
     * 获取当前时间并格式化
     * @return 当前时间对于格式的字符串。
     */
    public String getCurrentTime() {
        return dateFormat.format(new Date());
    }

    /**
     * 计算某个时刻到今天的年数差
     * @param fromTime 指定时间
     * @return 年数差
     */
    public int calcYearsDifferenceToToday(long fromTime) {
        return calcDaysDifferenceToToday(fromTime) / 365;
    }

    /**
     * 计算某个时刻到今天的天数差
     * @param dateString 指定时间，字符串
     * @return 天数差
     */
    public int calcDaysDifferenceToToday(String dateString) {
        Date fromDate = parse(dateString);
        if(fromDate == null) {
            return Integer.MAX_VALUE;
        }
        return calcDaysDifferenceToToday(fromDate);
    }

    /**
     * 计算某个时刻到今天的天数差
     * @param fromDate 指定时间 Date
     * @return 天数差
     */
    public int calcDaysDifferenceToToday(Date fromDate) {
        return calcDaysDifferenceToToday(fromDate.getTime());
    }

    /**
     * 计算某个时刻到今天的天数差
     * @param fromTime 指定时间 long
     * @return 天数差
     */
    public int calcDaysDifferenceToToday(long fromTime) {
        long unitDayToMs = (1000 * 60 * 60 * 24);
        // 修正不满1天的时间却在下一天的情况
        fromTime = (fromTime - (fromTime % unitDayToMs)) + 1;
        long timeDifference = calcTimeDifferenceToToday(fromTime);
        return (int) (timeDifference / unitDayToMs);  // 计算天
    }

    /**
     * 计算某个时刻到今天的时间差
     * @param fromTime 指定时间
     * @return 时间差
     */
    public long calcTimeDifferenceToToday(long fromTime) {
        long nowTime  = new Date().getTime();
        long unitDayToMs = (1000 * 60 * 60 * 24);
        // 修正不满1天的时间却在下一天的情况
        nowTime  = (nowTime  - (nowTime  % unitDayToMs)) + 1;

        return calcTimeDifference(fromTime, nowTime);
    }

    /**
     * 计算起止时间差
     * @param fromTime long 开始时间
     * @param toTime   long 终止时间
     * @return 时间差
     */
    public long calcTimeDifference(long fromTime, long toTime) {
        return toTime - fromTime;
    }

    /**
     * 断言到今天的时间还在 限制时间（limit） 内有效
     * @param fromTime 指定时间
     * @param limit 限制时间或过期时间
     * @return 如果在限制时间内，则有效返回 true，否则 false
     */
    public boolean assertToTodayValid(long fromTime, int limit) {
        return assertToTodayValid(fromTime, limit, true);
    }

    /**
     * 断言到今天的时间还在 限制时间（limit） 内有效
     * @param fromTime 指定时间
     * @param limit 限制时间或过期时间
     * @param isUnitDay 限制时间的单位，true 为天，false 为毫秒
     * @return 如果在限制时间内，则有效返回 true，否则 false
     */
    public boolean assertToTodayValid(long fromTime, int limit, boolean isUnitDay) {
        if(isUnitDay) {
            return calcDaysDifferenceToToday(fromTime) < limit;
        } else {
            return calcTimeDifference(fromTime, new Date().getTime()) < limit;
        }
    }

    /**
     * 断言到今天的时间还在 限制时间（limit） 内有效
     * @param fromTime 指定时间
     * @param limit 限制时间或过期时间
     * @return 如果在限制时间内，则有效返回 true，否则 false
     */
    public boolean assertToTodayValid(String fromTime, int limit) {
        return assertToTodayValid(fromTime, limit, true);
    }

    /**
     * 断言到今天的时间还在 限制时间（limit） 内有效
     * @param fromTime 指定时间
     * @param limit  限制时间或过期时间
     * @param isUnitDay 限制时间的单位，true 为天，false 为毫秒
     * @return 如果在限制时间内，则有效返回 true，否则 false
     */
    public boolean assertToTodayValid(String fromTime, int limit, boolean isUnitDay) {
        if(isUnitDay) {
            return calcDaysDifferenceToToday(fromTime) < limit;
        } else {
            Date fromDate = parse(fromTime);
            if(fromDate == null) {
                return false;
            }
            return calcTimeDifference(fromDate.getTime(), new Date().getTime()) < limit;
        }
    }

    /**
     * 检查时间字串是否合法有效，能被当前格式解析，或是一个有效日期。不能是0000-00-00之类。
     * @param fromTime 日期时间字串
     * @return true 有效  false 无效
     */
    public boolean valid(String fromTime) {
        if("0000-00-00".equals(fromTime) || "0000-00-00 00:00:00".equals(fromTime) || "".equals(fromTime)) {
            return false;
        }
        try {
            dateFormat.parse(fromTime).getTime();
            return true;
        } catch (ParseException e) {
            return false;
        }
    }

    /**
     * 解析字符串为Data类型
     * @param dataString 时间字符串
     * @return Date 时间
     */
    public Date parse(String dataString) {
        try {
            return dateFormat.parse(dataString);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 格式化 date 为对应格式
     * @param date 时间
     * @return String 时间
     */
    public String format(Date date) {
        return dateFormat.format(date);
    }

    /**
     * 将一个符合当前格式的时间字符串转换成目标格式的时间字符串
     * @param dateString 时间字符串
     * @param dateUtil 目标格式
     * @return 期望的时间字符串
     */
    public String to(String dateString, DateUtil dateUtil) {
        Date date = parse(dateString);
        if(date == null) {
            return "Invalid Date";
        }
        String dateStr = dateUtil.format(date);
        return dateStr;
    }

    private Function<Object, ?> transaction;

    private Object transaction(Object date) {
        if(date instanceof String) {
            return parse((String) date);
        }
        if(date instanceof Date) {
            return format((Date) date);
        }
        return date;
    }

    public DateUtil transaction(final DateUtil dateUtil) {
        Function<Object, ?> function = date -> dateUtil.transaction(date);
        transaction = transaction == null ? function : transaction.andThen(function);
        return this;
    }

    public <T,P> T runTransform(P date) {
        try {
            if(transaction != null) {
                return (T) transaction(transaction.apply(date));
            } else {
                return (T) transaction(date);
            }
        } finally {
            transaction = null;
        }
    }

//    public final class TimeWrapper {
//
//        private final long time;
//        private final TimeUnit timeUnit;
//
//        public TimeWrapper(long time) {
//            this(time, TimeUnit.MILLISECONDS);
//        }
//        public TimeWrapper(long time, TimeUnit timeUnit) {
//            this.time = time;
//            this.timeUnit = timeUnit;
//        }
//
//        public String year() {
//        }
//        public String month() {
//
//        }
//        public String day() {
//
//        }
//        public String hour() {
//
//        }
//        public String minute() {
//        }
//
//        public String second() {
//
//        }
//
//    }

}
